/*
 * Entrada.cpp
 *
 *  Created on: 02/10/2012
 */

#include "Entrada.h"

Entrada::Entrada(int numeroEstacionamiento, int numeroEntrada):
	numeroEntrada (numeroEntrada),
	entradaAbierta (true),
	numeroEstacionamiento (numeroEstacionamiento),
	colaEstacionamiento(Cola<MensajeTicket>((char*)ARCHIVO_COLAS_ESTACIONAMIENTOS, numeroEstacionamiento)){

	srand(getMilisegundos() + this->numeroEstacionamiento * 100 + this->numeroEntrada * 10);
	SignalHandler::getInstance()->registrarHandler(SIGUSR1, this);
	SignalHandler::getInstance()->registrarHandler(SIGINT, this);
	SignalHandler::getInstance()->registrarHandler(SIGTERM, this);
	
	Log::getInstance()->loguear("Entrada " + intToString(this->numeroEstacionamiento) + "." +
			intToString(this->numeroEntrada) + ": creada.");
}

Entrada::~Entrada() {
	SignalHandler::getInstance()->removerHandler(SIGUSR1);
	SignalHandler::getInstance()->removerHandler(SIGINT);
	SignalHandler::getInstance()->removerHandler(SIGTERM);
	SignalHandler::destruir();

	Log::getInstance()->loguear("Entrada " + intToString(this->numeroEstacionamiento) + "." +
			intToString(this->numeroEntrada) + ": destruida.");
}

/**
 * Handler de señales:
 * SIGUSR1: Señal que envia el terminador para finalizar la simulación.
 */
int Entrada::handleSignal(int signum){
	if (signum == SIGUSR1){
		Log::getInstance()->loguear("Entrada " + intToString(this->numeroEstacionamiento) + "." +
				intToString(this->numeroEntrada) + ": me llego SIGUSR1. Voy a finalizar.");
		this->entradaAbierta = false;

	} else if (signum == SIGINT){
		Log::getInstance()->loguear("Entrada " + intToString(this->numeroEstacionamiento) + "." +
				intToString(this->numeroEntrada) + ": me llego SIGINT. No hago nada.");
		// La señal la toma el terminador para detener la ejecución.

	} else if (signum == SIGTERM){
		Log::getInstance()->loguear("Entrada " + intToString(this->numeroEstacionamiento) + "." +
				intToString(this->numeroEntrada) + ": me llego SIGTERM. No hago nada.");
		// La señal la toma el terminador para detener la ejecución.

	}
	return 0;
}

/**
 * Duerme el proceso simulando la espera del arribo de un auto.
 * Devuelve -1 si llegó una señal.
 */
int Entrada::esperarAuto(){
	// Se usa usleep y se multiplica por el numero de estacionamiento y por el numero de la entrada
	// con el objetivo de que haya mas aleatoriedad entre entradas.

	return usleep(this->numeroEstacionamiento * 300000 + this->numeroEntrada * 50000
			+ calcularRandom(PERIODO_LLEGADA_AUTOS_MINIMO, PERIODO_LLEGADA_AUTOS_MAXIMO) * 1000000);
}

Auto Entrada::generarAuto(){
	Log::getInstance()->loguear("Entrada " + intToString(this->numeroEstacionamiento) + "." +
			intToString(this->numeroEntrada) + ": se genera un auto.");

	int horasEstacionado = calcularRandom(HORAS_ESTACIONADO_MINIMAS, HORAS_ESTACIONADO_MAXIMAS);
	Auto _auto(horasEstacionado);
	_auto.inicializar();
	return _auto;
}

/**
 * Simula la llegada de un auto al estacionamiento con una frecuencia aleatoria.
 */
int Entrada::recibirAutos(){

	while(this->entradaAbierta){

		if (this->esperarAuto() != -1){

			pid_t pid = fork();

			if (pid == 0){

				Auto _auto = this->generarAuto();
				Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEstacionamiento) + "." +
						intToString(this->numeroEntrada) + ": Llega un auto que va a estacionar " + intToString(_auto.getHorasEstacionado()) +
						" horas.");

				int lugarDisponible = this->pedirLugar();

				if (lugarDisponible != -1){
					Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEstacionamiento) + "." +
						intToString(this->numeroEntrada) + ": El auto estaciona en el lugar " + intToString(lugarDisponible) + ".");

					_auto.setTicket(Ticket(lugarDisponible, _auto.getHorasEstacionado(), this->numeroEstacionamiento));
					_auto.estacionar();

				} else {
					Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEstacionamiento) + "." +
							intToString(this->numeroEntrada) + ": No hay lugares disponibles.");
				}

				// Si no hay lugar disponible el proceso del auto se muere.
				return 1;
			}
		}
	}
	Log::getInstance()->imprimir("Entrada " + intToString(this->numeroEstacionamiento) + "." +
			intToString(this->numeroEntrada) + ": No se aceptan más autos.");
	return 0;
}

/**
 * Metodo que envia un mensaje al estacionamiento para solicitar lugar.
 */
int Entrada::pedirLugar(){
	MensajeTicket mensaje;
	mensaje.mtype = ENTRADA_PIDE_LUGAR_P;
	mensaje.ticket.setNumeroEstacionamiento(this->numeroEstacionamiento);
	colaEstacionamiento.escribir(mensaje);

	int resultado = colaEstacionamiento.leer(ENTRADA_PIDE_LUGAR_R, &mensaje);
	if (resultado == -1){
		Log::getInstance()->loguear("Entrada " + intToString(this->numeroEstacionamiento) + "." +
			intToString(this->numeroEntrada) + ": error leyendo la cola al pedir un lugar disponible.");
	}
	return mensaje.ticket.getLugarOcupado();
}
